﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Commands.Scheduling
{
    class SuspendTimer
    {
        public SuspendTimer(ISchedule _schedule)
        {
            this._schedule = _schedule;

            // Init suspending.
            _schedule.SuspendSettingsChanged += () => ScheduleSuspend();
            ScheduleSuspend();
        }

        /// <summary>
        /// Start new suspending cycle.
        /// </summary>
        public void ScheduleSuspend()
        {
            _ScheduleSuspend(_schedule.TimeToSuspend);
        }

        /// <summary>
        /// Hibernate computer.
        /// </summary>
        /// <param name="timeTosuspend">Time after which computer will be suspended.</param>
        private void _ScheduleSuspend(TimeSpan timeToSuspend)
        {
            Debug.Assert(timeToSuspend > TimeSpan.Zero);

            // Check that suspending is allowed.
            if (!_schedule.SuspendEnabled)
                _CancelSuspend();
            else
            {
                if (_schedule.SuspendOnlyOnInactive)
                    _ScheduleInactiveSuspend(timeToSuspend);
                else
                    _StartCommonTimer(timeToSuspend);
            }
        }

        private void _PutToSuspend()
        {
            _suspendTimer.Stop();

            // If schedule allow to suspend right now - suspend.
            if (_schedule.CanBeSuspendedNow())
                Suspender.SuspendNow(_schedule.GetWakeupTime(DateTime.Now));
            // Otherwise - start new suspending cycle.
            else
                ScheduleSuspend();
        }

        private void _StartCommonTimer(TimeSpan timeToSuspending)
        {
            // Update schedule suspend time.
            _schedule.SuspendTime = DateTime.Now + timeToSuspending;

            _suspendTimer = new System.Timers.Timer();
            _suspendTimer.Interval = timeToSuspending.TotalMilliseconds;
            _suspendTimer.Elapsed += (_, __) => _PutToSuspend();
            _suspendTimer.Start();
        }

        /// <summary>
        /// Schedule suspend after inactivity time.
        /// </summary>
        /// <param name="inactiveTimeToSuspend">Time in which user has no activity. 
        /// After it computer should be suspended.</param>
        private void _ScheduleInactiveSuspend(TimeSpan inactiveTimeToSuspend)
        {
            // Calculate time which is left to stay in inactivity before computer could be suspended.
            var timeLeftForInactive =
                inactiveTimeToSuspend - (TimeSpan)SystemIdleInformator.GetInactiveTime();

            if (timeLeftForInactive > INACTIVITY_TIMER_CHECK_PERIOD)
                _StartInactiveTimer(timeLeftForInactive, inactiveTimeToSuspend);
            else
                _PutToSuspend();
        }

        private void _StartInactiveTimer(TimeSpan timeLeftForInactive, TimeSpan inactivityTimeForSuspend)
        {
            _schedule.SuspendTime = DateTime.Now + timeLeftForInactive;

            _suspendTimer = new System.Timers.Timer(INACTIVITY_TIMER_CHECK_PERIOD.TotalMilliseconds);
            _suspendTimer.Elapsed += (_, __) =>
            {
                _ScheduleSuspend(inactivityTimeForSuspend);
                _suspendTimer.Stop();
            };
            _suspendTimer.Start();
        }

        private void _CancelSuspend()
        {
            if(_suspendTimer != null)
                _suspendTimer.Stop();

            Suspender.RemoveWakeUpTask();
        }

        private ISchedule _schedule;
        private System.Timers.Timer _suspendTimer;

        private static readonly TimeSpan INACTIVITY_TIMER_CHECK_PERIOD = TimeSpan.FromSeconds(1);
    }
}
